﻿using System;
using System.Collections;

namespace Bouyei.Geo.Geometries
{
    public class GeoSequence:IEnumerator,IEnumerable
    {
        private Coordinate[] sets { get; set; }
        private int position = 0;

        public int Count { get { return sets.Length; } }

        public GeoDirection Direction { get; private set; }

        public GeoType GeometryType { get; set; }

        public Coordinate this[int index]
        {
            get { return sets[index]; }
            set { sets[index] = value; }
        }

        public GeoPoint GetPoint(int index)
        {
            return new GeoPoint(sets[index]);
        }
      
        public IEnumerator GetEnumerator()
        {
            return (IEnumerator)this;
        }

        public void Reset()
        {
            position = 0;
        }

        public bool MoveNext()
        {
            ++position;

            return (position < sets.Length);
        }

        public object Current
        {
            get
            {
                return sets[position];
            }
        }

        public Coordinate[] GetCoordinates()
        {
            return sets;
        }

        public GeoSequence(GeoDirection direction,GeoType geometryType)
        {
            this.GeometryType = geometryType;
            this.Direction = direction;
        }

        public GeoSequence(Coordinate coordiante)
            :this(GeoDirection.Clockwise,GeoType.POINT)
        {
            this.sets = new Coordinate[1];
            this.sets[0] = coordiante;
        }

        public GeoSequence(Coordinate[] sets,GeoType  geometryType,
            GeoDirection direction=GeoDirection.Clockwise)
            :this(direction,geometryType)
        {
            this.sets = sets;
        }

        public GeoSequence(GeoPoint[] geoPoints, GeoType geometryType,
            GeoDirection direction=GeoDirection.Clockwise)
            :this(direction,geometryType)
        {
            this.sets = new Coordinate[geoPoints.Length];
            for (int i = 0; i < sets.Length; ++i)
            {
                this.sets[i] = new Coordinate(geoPoints[i].X, geoPoints[i].Y);
            }
        }

        public GeoSequence(GeoLinestring geoLinestring, GeoType geometryType,
            GeoDirection direction=GeoDirection.Clockwise)
            :this(direction,geometryType)
        {
            this.sets = new Coordinate[geoLinestring.GetSequence(0, 0).Count];
            for (int i = 0; i < sets.Length; ++i)
            {
                this.sets[i] = geoLinestring[i];
            }
        }

        public GeoSequence(GeoLineSegment[] lineSegments)
        {
            GeometryType = GeoType.LINESTRING;
            sets = new Coordinate[lineSegments.Length + 1];
            sets[0] = new Coordinate()
            {
                X = lineSegments[0].Start.X,
                Y = lineSegments[0].Start.Y,
                Z = lineSegments[0].Start.Z
            };
            for (int i = 1; i < sets.Length; ++i)
            {
                sets[i] = new Coordinate()
                {
                    X = lineSegments[i].End.X,
                    Y = lineSegments[i].End.Y,
                    Z = lineSegments[i].End.Z
                };
            }
        }

        public GeoLineSegment GetBoundrayBox()
        {
            int len = sets.Length;
            Coordinate min = sets[0].Copy(), max = sets[0].Copy();

            for (int i = 1; i < len; ++i)
            {
                min.X = min.MinX(sets[i]);
                min.Y = min.MinY(sets[i]);

                max.X = max.MaxX(sets[i]);
                max.Y = max.MaxY(sets[i]);
            }
            return new GeoLineSegment (min, max);
        }

        public bool Intersects(GeoSequence sequence)
        {
            for (int i = 0; i < sets.Length; ++i)
            {
                var line1 = GetLineSegment(i);

                for (int j = 0; j < sequence.Count; ++j)
                {
                    var line2 = sequence.GetLineSegment(j);

                    if (line1.DiagonalIntersects(line2) == false) continue;

                    bool hasCollinear= line1.IsCollinear(line2);
                    if (hasCollinear) continue;

                    var intersect= line1.Intersects(line2);

                }
            }
            return false;
        }

        public GeoLineSegment GetLineSegment(int startIndex)
        {
            return new GeoLineSegment(sets[startIndex], sets[startIndex + 1]);
        }

        public bool IsRing()
        {
            int index = sets.Length - 1;

            return sets[0].Equals(sets[index]);
        }

        public void Insert(int position,Coordinate coord)
        {
            Coordinate[] coords = new Coordinate[sets.Length + 1];
            for (int i = 0; i < coords.Length; ++i)
            {
                if (i == position) coords[i] = coord;
                else coords[i] = sets[i];
            }
            sets = coords;
        }

        public void Add(Coordinate coord)
        {
            Insert(sets.Length, coord);
        }

        public void Add(GeoPoint point)
        {
            Insert(sets.Length, new Coordinate(point.X, point.Y));
        }
    }

    public enum GeoDirection
    {
        Unknown,
        Clockwise,
        AntiClockwise
    }
}
